/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.nukc.stateview.view;

import ohos.accessibility.ability.GestureResultListenerInfo;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.ComponentState;
import ohos.agp.components.ListContainer;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.util.NoSuchElementException;

/**
 * MaterialRippleLayout
 *
 * @since 2021-04-26
 */
public class MaterialRippleLayout extends StackLayout
        implements Component.TouchEventListener,Component.DrawTask,Component.ClickedListener {
    static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MaterialRippleLayout");
    private static final boolean IS_DEFAULT_DELAY_CLICK = true;
    private static final float DEFAULT_ALPHA = 0.2f;
    private static final int DEFAULT_DURATION = 350;
    private static final float DEFAULT_DIAMETER_DP = 80;
    private static final int HOVER_DURATION = 1500;
    private static final int ADD_RADIUS_LENGTH = 20;
    private int rippleDuration = DEFAULT_DURATION;
    private static final boolean IS_DEFAULT_RIPPLE_OVERLAY = false;
    private static final int DEFAULT_RIPPLE_COLOR = 0x80000000;
    private static final int INTAGER2 = 2;
    private static final int INTAGER_SIX = 6;
    private static final int INTAGER3 = 3;
    private static final int TIME = 200;

    MmiPoint pointerPosition;
    DelayRunAnimator delayRunAnimator = new DelayRunAnimator();

    private final AnimatorValue.ValueUpdateListener mAnimatorUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorV, float value) {
            if (value <= 0) {
                return;
            }
            float currentRadius = rippleDiameter + maxRadius * value;
            if (currentRadius < rippleDiameter) {
                setRadius(rippleDiameter);
            } else {
                setRadius(currentRadius);
            }
            invalidate();
        }
    };

    private Component childView;
    private Color rippleColor = Color.BLACK;
    private float rippleAlpha = DEFAULT_ALPHA;
    private float rippleDiameter = DEFAULT_DIAMETER_DP;
    private boolean isRippleOverlay;
    private ShapeElement mBackgroundUnable;
    private ShapeElement mBackgroundPressed;

    private final Paint mPaint = new Paint();


    private EventHandler handler;

    private AnimatorValue animatorValue;

    private ListContainer parentAdapter;

    private boolean isLongClick = false;
    private float maxRadius;
    private float mRadiusRipple;
    private boolean isFingerUp = true;
    private float positionX;
    private float positionY;
    private OnClickButton mclickButton;

    /** 构造器
     *
     * @param context 上下文
     */
    public MaterialRippleLayout(Context context) {
        super(context);
        init(context, null, "");
    }

    /** 构造器
     *
     * @param context 上下文
     * @param attrSet 属性集合
     */
    public MaterialRippleLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(context, attrSet, "");
    }

    /** 构造器
     *
     * @param context 上下文
     * @param attrSet 属性集合
     * @param styleName 样式名称
     */
    public MaterialRippleLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context, attrSet, styleName);
    }

    public float getX() {
        return positionX;
    }

    public void setX(float ppx) {
        this.positionX = ppx;
    }

    public float getY() {
        return positionY;
    }

    public void setY(float ppy) {
        this.positionY = ppy;
    }

    public float getRadius() {
        return mRadiusRipple;
    }

    public void setRadius(float radius) {
        this.mRadiusRipple = radius;
    }

    /** 实例化RippleBuilder
     * @param view Button组件
     * @return RippleBuilder RippleBuilder对象
     */
    public RippleBuilder on(Button view) {
        return new RippleBuilder(view);
    }

    private void init(Context context, AttrSet attrs, String defStyleAttr) {
        handler = new EventHandler(EventRunner.getMainEventRunner());
        initAttrs(context, attrs, defStyleAttr);
        setupPaint();

        addDrawTask(this::onDraw);
        setTouchEventListener(this::onTouchEvent);
    }

    private void initAttrs(Context context, AttrSet attrSet, String defStyleAttr) {
        setStatePress();
        if (attrSet == null) {
            return;
        }
        rippleColor = TypedAttrUtils.getColor(attrSet, "mrl_rippleColor", new Color(DEFAULT_RIPPLE_COLOR));
        rippleAlpha = TypedAttrUtils.getFloat(attrSet, "mrl_rippleAlpha", DEFAULT_ALPHA);
        rippleDuration = TypedAttrUtils.getInteger(attrSet, "mrl_rippleDuration", DEFAULT_DURATION);
        rippleDiameter = TypedAttrUtils.getFloat(attrSet, "mrl_rippleDiameterDp", DEFAULT_DIAMETER_DP);
        isRippleOverlay = TypedAttrUtils.getBoolean(attrSet, "mrl_rippleOverlay", IS_DEFAULT_RIPPLE_OVERLAY);
    }

    /**
     * 初始化画笔
     */
    private void setupPaint() {
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(rippleColor);
        mPaint.setAlpha(rippleAlpha);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        canvas.drawCircle(getX(), getY(), getRadius(), mPaint);
    }

    private void setStatePress() {
        mBackgroundUnable = new ShapeElement();
        mBackgroundUnable.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));
        mBackgroundPressed = new ShapeElement();
        mBackgroundPressed.setRgbColor(RgbColor.fromArgbInt(Color.BLUE.getValue()));
        int[][] states = new int[INTAGER_SIX][];
        states[0] = new int[]{ComponentState.COMPONENT_STATE_DISABLED};
        states[1] = new int[]{ComponentState.COMPONENT_STATE_PRESSED};
        states[INTAGER2] = new int[]{ComponentState.COMPONENT_STATE_FOCUSED};
        states[INTAGER3] = new int[]{ComponentState.COMPONENT_STATE_EMPTY};
        StateElement stateBackground = new StateElement();
        stateBackground.addState(states[0], mBackgroundUnable);
        stateBackground.addState(states[1], mBackgroundPressed);
        stateBackground.addState(states[INTAGER2], mBackgroundPressed);
        stateBackground.addState(states[INTAGER3], mBackgroundUnable);
    }

    /**
     * 点击事件的动画
     */
    private void initAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorValue.setDuration(rippleDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                setRadius(0);
                fingerUp();

                if (mclickButton != null) {
                    mclickButton.onClick();
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }

    public void setOnClick(OnClickButton clickButton) {
        mclickButton = clickButton;
    }

    /**
     * OnClickButton
     *
     * @since 2021-04-26
     */
    public interface OnClickButton {
        /** 点击事件
         */
        void onClick();
    }

    /**
     * 长按事件动画
     */
    private void initLongClickAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorValue.setDuration(rippleDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                if (isRippleOverlay && isFingerUp) {
                    setRadius(0);
                    invalidate();
                }
                if (!isRippleOverlay) {
                    setRadius(0);
                    invalidate();
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }

    @Override
    public void addComponent(Component childComponent) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("");
        }
        childView = childComponent;
        super.addComponent(childComponent);
    }

    @Override
    public void setClickedListener(ClickedListener listener) {
        if (childView == null) {
            throw new IllegalStateException("");
        }
        childView.setClickedListener(listener);
    }

    @Override
    public void setLongClickedListener(LongClickedListener listener) {
        if (childView == null) {
            throw new IllegalStateException("");
        }
        childView.setLongClickedListener(listener);
    }

    @Override
    public void onClick(Component component) {
        isLongClick = false;
        rippleDuration = DEFAULT_DURATION;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        touchEvent.getRadius(0);
        pointerPosition = touchEvent.getPointerPosition(0);
        float ppx = pointerPosition.getX();
        int[] parentLocationOnScreen = getLocationOnScreen();

        setX(ppx - parentLocationOnScreen[0]);
        setY((float) component.getHeight() / INTAGER2);
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                isFingerUp = false;
                rippleDuration = DEFAULT_DURATION;
                handler.postTask(delayRunAnimator, TIME);
                setRippleDuration(rippleDuration);

                if (Math.abs(ppx - parentLocationOnScreen[0])
                        > Math.abs(ppx - (parentLocationOnScreen[0] + getWidth()))) {
                    maxRadius = Math.abs(ppx - parentLocationOnScreen[0]) + ADD_RADIUS_LENGTH;
                } else {
                    maxRadius = Math.abs(ppx - (parentLocationOnScreen[0] + getWidth())) + ADD_RADIUS_LENGTH;
                }
                mRadiusRipple = maxRadius;
                fingerPress();
                resetConfig();
                return true;
            case TouchEvent.POINT_MOVE:
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                isFingerUp = true;
                if (!isLongClick) {
                    handler.removeTask(delayRunAnimator);
                    if (rippleDuration <= 0) {
                        rippleDuration = DEFAULT_DURATION;
                    }
                    setRippleDuration(rippleDuration);
                    initAnimator();
                }
                if (isLongClick) {
                    fingerUp();
                }
                break;
            default:
                break;
        }
        return false;
    }

    /**
     * DelayRunAnimator
     *
     * @since 2021-04-26
     */
    private class DelayRunAnimator implements Runnable {
        @Override
        public void run() {
            isLongClick = true;
            rippleDuration = HOVER_DURATION;
            initLongClickAnimator();
        }
    }

    /**
     * 点击操作
     */
    private void performalViewClick() {
        if (getComponentParent() instanceof ListContainer) {
            if (!childView.callOnClick()) {
                clickAdapterView((ListContainer) getComponentParent());
            }
        } else {
            if (childView != null) {
                childView.callOnClick();
            }
        }
    }

    private void clickAdapterView(ListContainer parent) {
        final int position = parent.getIndexForComponent(MaterialRippleLayout.this);
        final long itemId = parent.getItemProvider() != null
                ? parent.getItemProvider().getItemId(position)
                : 0;
        if (position != ListContainer.INVALID_INDEX) {
            parent.executeItemClick(MaterialRippleLayout.this, position, itemId);
        }
    }

    /**
     * PerformClickEvent
     *
     * @since 2021-04-26
     */
    private class PerformClickEvent implements Runnable {
        @Override
        public void run() {
            if (getComponentParent() instanceof ListContainer) {
                if (!childView.callOnClick()) {
                    clickAdapterView((ListContainer) getComponentParent());
                }
            } else {
                if (childView != null) {
                    childView.callOnClick();
                }
                callOnClick();
            }
        }

        private void clickAdapterView(ListContainer parent) {
            final int position = parent.getIndexForComponent(MaterialRippleLayout.this);
            final long itemId = parent.getItemProvider() != null
                    ? parent.getItemProvider().getItemId(position)
                    : 0;
            if (position != ListContainer.INVALID_INDEX) {
                parent.executeItemClick(MaterialRippleLayout.this, position, itemId);
            }
        }
    }

    private ListContainer findParentAdapterView() {
        if (parentAdapter != null) {
            return parentAdapter;
        }
        ComponentParent current = getComponentParent();
        while (true) {
            if (current instanceof ListContainer) {
                parentAdapter = (ListContainer) current;
                return parentAdapter;
            } else {
                try {
                    current = current.getComponentParent();
                } catch (NoSuchElementException e) {
                    HiLog.info(LABEL, "Exception = " + e.toString());
                }
            }
        }
    }

    private void resetConfig() {
        setRadius(getRadius());
    }

    /**
     * 按下
     */
    private void fingerPress() {
    }

    /**
     * 抬起
     */
    private void fingerUp() {
        if (isRippleOverlay && isFingerUp) {
            setRadius(0);
            invalidate();
        }
        isLongClick = false;
    }

    /** 设置波纹颜色
     * @param rippleColor 波纹颜色
     */
    public void setRippleColor(int rippleColor) {
        this.rippleColor = new Color(rippleColor);
        mPaint.setColor(new Color(rippleColor));
        mPaint.setAlpha(rippleAlpha);
        invalidate();
    }

    /**
     * 设置透明度
     *
     * @param alpha alpha
     */
    public void setDefaultRippleAlpha(float alpha) {
        this.rippleAlpha = alpha;
        mPaint.setAlpha(rippleAlpha);
        invalidate();
    }

    /** 判断波纹是否被点击
     * @param isClick 布尔值
     */
    public void isRippleDelayClick(boolean isClick) {
    }

    public void setRippleDiameter(int rippleDiameter) {
        this.rippleDiameter = rippleDiameter;
    }

    public void setRippleDuration(int rippleDuration) {
        this.rippleDuration = rippleDuration;
    }

    /** 是否波纹叠加
     *
     * @param isOverlay 布尔值
     */
    public void isRippleOverlay(boolean isOverlay) {
        this.isRippleOverlay = isOverlay;
    }

    /** 设置波纹背景
     *
     * @param color 颜色
     */
    public void setRippleBackground(RgbColor color) {
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(color);
        setBackground(shapeElement);
    }

    /**
     * RippleBuilder
     *
     * @since 2021-04-26
     */
    public static class RippleBuilder {
        private static final int RED = 255;
        private static final int GREEN = 255;
        private static final int BLUE = 255;
        private final Context context;
        private final Component child;

        private int rippleColor = Color.rgb(0, 0, 0);
        private boolean isRippleOverlay = IS_DEFAULT_RIPPLE_OVERLAY;
        private float rippleDiameter = DEFAULT_DIAMETER_DP;
        private int rippleDuration = DEFAULT_DURATION;
        private float rippleAlpha = DEFAULT_ALPHA;
        private boolean isRippleDelayClick = IS_DEFAULT_DELAY_CLICK;
        private int rippleBackground = Color.rgb(RED, GREEN, BLUE);

        /** 构造器
         *
         * @param child 子组件
         */
        public RippleBuilder(Button child) {
            this.child = child;
            this.context = child.getContext();
        }

        /** 设置RippleBuilder对象颜色并返回该对象
         *
         * @param color 颜色
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleColor(int color) {
            this.rippleColor = color;
            return this;
        }

        /** 设置RippleBuilder对象是否波纹叠加并返回该对象
         *
         * @param isOverlay 布尔值
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleOverlay(boolean isOverlay) {
            this.isRippleOverlay = isOverlay;
            return this;
        }

        /** 设置RippleBuilder对象波纹直径并返回该对象
         *
         * @param diameterDp 波纹直径
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleDiameterDp(int diameterDp) {
            this.rippleDiameter = diameterDp;
            return this;
        }

        /** 设置RippleBuilder对象波纹持续时间并返回该对象
         *
         * @param duration 波纹持续时间
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleDuration(int duration) {
            this.rippleDuration = duration;
            return this;
        }

        /** 设置RippleBuilder对象波纹透明度并返回该对象
         *
         * @param alpha 颜色
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleAlpha(float alpha) {
            this.rippleAlpha = alpha;
            return this;
        }

        /** 设置RippleBuilder对象波纹背景并返回该对象
         *
         * @param color 颜色
         * @return RippleBuilder RippleBuilder对象
         */
        public RippleBuilder rippleBackground(int color) {
            this.rippleBackground = color;
            return this;
        }

        /** 创建MaterialRippleLayout对象并设置属性
         *
         * @return MaterialRippleLayout MaterialRippleLayout
         */
        public MaterialRippleLayout create() {
            MaterialRippleLayout layout = new MaterialRippleLayout(context);
            layout.setRippleColor(rippleColor);
            layout.setDefaultRippleAlpha(rippleAlpha);
            layout.isRippleDelayClick(isRippleDelayClick);
            layout.setRippleDiameter(AttrHelper.fp2px(rippleDiameter, context));
            layout.setRippleDuration(rippleDuration);
            layout.isRippleOverlay(isRippleOverlay);
            layout.setRippleBackground(new RgbColor(rippleBackground));

            ShapeElement shapeElement = new ShapeElement();
            shapeElement.setRgbColor(RgbColor.fromArgbInt(rippleBackground));
            child.setBackground(shapeElement);

            ComponentContainer.LayoutConfig params = child.getLayoutConfig();
            ComponentContainer parent = (ComponentContainer) child.getComponentParent();
            int index = 0;

            if (parent != null) {
                index = parent.getChildIndex(child);
                parent.removeComponent(child);
            }

            layout.addComponent(child,
                    new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                            ComponentContainer.LayoutConfig.MATCH_PARENT));

            if (parent != null) {
                parent.addComponent(layout, index, params);
            }
            return layout;
        }
    }
}
